/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.task.zkex.simple;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.GetListRequestMessage;
import cn.easyplatform.messages.response.GetListResponseMessage;
import cn.easyplatform.messages.vos.GetListVo;
import cn.easyplatform.spi.service.ComponentService;
import cn.easyplatform.type.FieldVo;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.web.ext.ComponentBuilder;
import cn.easyplatform.web.ext.Widget;
import cn.easyplatform.web.ext.zul.Timeslots;
import cn.easyplatform.web.service.ServiceLocator;
import cn.easyplatform.web.task.OperableHandler;
import cn.easyplatform.web.task.event.EventListenerHandler;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.*;

import java.text.DecimalFormat;
import java.util.*;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class TimeslotsBuilder implements ComponentBuilder, Widget,
        EventListener<Event> {

    private Timeslots tms;

    private OperableHandler main;

    private double[] slots;

    public TimeslotsBuilder(OperableHandler mainTaskHandler, Timeslots tms) {
        this.main = mainTaskHandler;
        this.tms = tms;
    }

    @Override
    public Component build() {
        if (0 > tms.getBeginTime() || tms.getBeginTime() > 24)
            throw new EasyPlatformWithLabelKeyException(
                    "component.property.not.found", "<timeslots>", "beginTime");
        if (0 > tms.getEndTime() || tms.getEndTime() > 24)
            throw new EasyPlatformWithLabelKeyException(
                    "component.property.not.found", "<timeslots>", "endTime");
        if (tms.getBeginTime() > tms.getEndTime())
            throw new EasyPlatformWithLabelKeyException(
                    "component.property.not.found", "<timeslots>",
                    "begin time shall not later then end time");
        if (tms.getSlots() != 1 && tms.getSlots() != 2 && tms.getSlots() != 4
                && tms.getSlots() != 6)
            throw new EasyPlatformWithLabelKeyException(
                    "component.property.not.found", "<timeslots>", "slots");
        if (Strings.isBlank(tms.getQuery()))
            throw new EasyPlatformWithLabelKeyException(
                    "component.property.not.found", "<timeslots>", "query");
        tms.setOddRowSclass("none");
        Columns cols = new Columns();
        cols.setSizable(true);
        StringBuilder sb = new StringBuilder();
        slots = new double[(tms.getEndTime() - tms.getBeginTime())
                * tms.getSlots()];
        for (int i = tms.getBeginTime(), index = 0; i < tms.getEndTime(); i++) {
            for (int j = 0; j < tms.getSlots(); j++) {
                sb.setLength(0);
                int v = j * (60 / tms.getSlots());
                if (i > 9)
                    sb.append(i);
                else
                    sb.append("0").append(i);
                sb.append(":");
                if (v > 9)
                    sb.append(v);
                else
                    sb.append("0").append(v);
                Column col = new Column(tms.getSlots() == 1 ? sb.toString()
                        : "");
                col.setAlign("center");
                cols.appendChild(col);
                slots[index++] = i + v * 0.01;
            }
        }
        if (tms.getSlots() > 1) {
            Auxhead ah = new Auxhead();
            for (int i = tms.getBeginTime(); i < tms.getEndTime(); i++) {
                sb.setLength(0);
                if (i > 9)
                    sb.append(i);
                else
                    sb.append("0").append(i);
                sb.append(":00");
                Auxheader header = new Auxheader(sb.toString());
                header.setAlign("center");
                header.setColspan(tms.getSlots());
                header.setParent(ah);
            }
            ah.setParent(tms);
        }
        sb = null;
        tms.appendChild(cols);
        tms.setAttribute("$proxy", this);
        createItem();
        return tms;
    }

    private void createItem() {
        ComponentService cs = ServiceLocator
                .lookup(ComponentService.class);
        GetListVo gv = new GetListVo(tms.getDbId(), tms.getQuery());
        gv.setFilter(tms.getFilter());
        GetListRequestMessage req = new GetListRequestMessage(main.getId(), gv);
        IResponseMessage<?> resp = cs.getList(req);
        if (resp.isSuccess()) {
            List<FieldVo[]> data = (List<FieldVo[]>) ((GetListResponseMessage) resp).getBody();
            if (!data.isEmpty() && data.get(0).length < 4)
                throw new EasyPlatformWithLabelKeyException(
                        "component.combo.match", "<timeslots>", "query", "size");
            Rows rows = null;
            if (tms.getRows() == null) {
                rows = new Rows();
                tms.appendChild(rows);
            } else {
                rows = tms.getRows();
                rows.getChildren().clear();
            }
            Map<Object, List<FieldVo[]>> tmp = new LinkedHashMap<Object, List<FieldVo[]>>();
            for (FieldVo[] fvs : data) {
                List<FieldVo[]> vos = tmp.get(fvs[0].getValue());
                if (vos == null) {
                    vos = new ArrayList<FieldVo[]>();
                    tmp.put(fvs[0].getValue(), vos);
                }
                vos.add(fvs);
            }
            DecimalFormat format = new DecimalFormat("#0.00");
            for (Map.Entry<Object, List<FieldVo[]>> entry : tmp.entrySet()) {
                Row row = new Row();
                row.setTooltiptext(entry.getKey().toString());
                List<FieldVo[]> list = new ArrayList<FieldVo[]>(
                        entry.getValue());
                Collections.sort(list, new Comparator<FieldVo[]>() {
                    @Override
                    public int compare(FieldVo[] o1, FieldVo[] o2) {
                        double d = getTime(o1[1].getValue())
                                - getTime(o2[1].getValue());
                        if (d > 0)
                            return 1;
                        else if (d == 0d)
                            return 0;
                        else
                            return -1;
                    }
                });
                for (int i = 0; i < slots.length; ) {
                    Cell c = new Cell();
                    c.setParent(row);
                    Iterator<FieldVo[]> itr = list.iterator();
                    boolean search = false;
                    while (itr.hasNext()) {
                        FieldVo[] fvs = itr.next();
                        double beginTime = getTime(fvs[1].getValue());
                        if (slots[i] == beginTime) {
                            double endTime = getTime(fvs[2].getValue());
                            String val = (fvs[2].getValue() instanceof Date) ? format
                                    .format(endTime - beginTime) : format
                                    .format(endTime);
                            int cols = Integer.parseInt(val.substring(0,
                                    val.indexOf(".")));
                            int mins = Integer.parseInt(val.substring(val
                                    .indexOf(".") + 1));
                            if (tms.getSlots() > 1) {
                                cols *= tms.getSlots();
                                if (mins > 0)
                                    cols += mins / (60 / tms.getSlots());
                            }
                            if (cols > 0)
                                c.setColspan(cols);
                            c.setStyle("padding:4px 0px !important");
                            A btn = new A();
                            btn.setLabel(fvs[3].getValue() == null ? ""
                                    : fvs[3].getValue().toString());
                            if (fvs.length > 4)
                                btn.setZclass((fvs[4].getValue() == null ? "btn btn-info"
                                        : "btn " + fvs[4].getValue())
                                        + " btn-xs");
                            else
                                btn.setZclass("btn btn-info btn-xs");

                            if (Strings.isBlank(tms.getItemStyle()))
                                btn.setStyle(tms.getItemStyle());
                            btn.setHflex("1");
                            //btn.setHeight("22px");
                            btn.setAttribute("value", fvs);
                            if (!Strings.isBlank(tms.getEvent()))
                                btn.addEventListener(Events.ON_CLICK, this);
                            btn.setParent(c);
                            i += cols;
                            itr.remove();
                            search = true;
                            break;
                        }
                    }
                    if (!search)
                        i++;
                }
                row.setParent(rows);
            }
        } else
            Clients.wrongValue(tms, (String) resp.getBody());
    }

    @SuppressWarnings("deprecation")
    private double getTime(Object val) {
        if (val instanceof Date) {
            Date date = (Date) val;
            return date.getHours() + date.getMinutes() * 0.01;
        } else if (val instanceof Number) {
            return ((Number) val).doubleValue();
        } else
            return Double.parseDouble(val.toString().replace(":", "."));
    }

    @Override
    public void onEvent(Event event) throws Exception {
        FieldVo[] fvs = (FieldVo[]) event.getTarget().getAttribute("value");
        Object[] data = new Object[fvs.length];
        for (int i = 0; i < data.length; i++)
            data[i] = fvs[i].getValue();
        EventListenerHandler elh = new EventListenerHandler(event.getName(), main,
                tms.getEvent());
        elh.onEvent(new Event(event.getName(), event.getTarget(), data));
    }

    @Override
    public void reload(Component widget) {
        createItem();
    }
}
